const TYPE = {
    isString: o => Object.prototype.toString.call(o) === "[object String]",
    isNumber: o => Object.prototype.toString.call(o) === "[object Number]",
    isArrayBuffer: o => Object.prototype.toString.call(o) === "[object ArrayBuffer]",
    isUint8Array: o => Object.prototype.toString.call(o) === "[object Uint8Array]",
    isArray: o => Object.prototype.toString.call(o) === "[object Array]",
    isNull: o => Object.prototype.toString.call(o) === "[object Null]",
    isUndefined: o => Object.prototype.toString.call(o) === "[object Undefined]",
};

/**
 * 普通字符串转二进制
 * @param s
 * @returns {Uint8Array}
 */
function stob(s) {
    let escstr = encodeURIComponent(s);
    let binstr = escstr.replace(/%([0-9A-F]{2})/g, (m, p) => String.fromCharCode('0x' + p));
    let ui8 = new Uint8Array(binstr.length);
    Array.prototype.forEach.call(binstr, (d, i) => ui8[i] = d.charCodeAt(0));
    return ui8;
}

/**
 * 二进制转字符串
 * @param {Uint8Array} b
 * @returns {string}
 */
function btos(b) {
    if(!TYPE.isUint8Array(b)) {
        console.error("方法btos需要一个Uint8Array类型的参数！");
        return "";
    }

    let binstr = Array.prototype.map.call(b, (d, i) => {
        let c = d.toString(16).toUpperCase();
        if (c.length < 2) {
            c = '0' + c;
        }
        return '%' + c;
    }).join('');
    return decodeURIComponent(binstr);
}

/**
 * 文件转二进制
 * @param {File} f
 * @returns {Promise<Uint8Array>}
 */
function ftob(f) {
    let s = f.size;     // 文件大小
    let t = f.type;     // 文件类型
    let n = f.name;     // 文件名

    let tb = stob(t);
    let nb = stob(n);

    return new Promise(resolve => f.arrayBuffer().then(buf => {
        let ab = new ArrayBuffer(4 + 1 + tb.byteLength + 1 + nb.byteLength + buf.byteLength);
        let dv = new DataView(ab);
        let ui8 = new Uint8Array(ab);
        let offset = 0;

        // 文件大小
        dv.setUint32(offset, s, false);
        offset += 4;

        // 文件类型
        dv.setUint8(offset++, tb.byteLength);
        ui8.set(new Uint8Array(tb), offset);
        offset += tb.byteLength;
        // tb.forEach(d => dv.setUint8(offset++, d));

        // 文件名
        dv.setUint8(offset++, nb.byteLength);
        ui8.set(new Uint8Array(nb), offset);
        offset += nb.byteLength;
        // nb.forEach(d => dv.setUint8(offset++, d));

        // 文件数据
        ui8.set(new Uint8Array(buf), offset);
        offset += buf.byteLength;

        resolve(ui8);
    }));
}

/**
 * 二进制转文件
 * @param {ArrayBuffer} b
 */
function btof(b) {
    if(!TYPE.isArrayBuffer(b)) {
        console.error("btof方法的参数类型为ArrayBuffer");
        return null;
    }

    let dv = new DataView(b);
    let offset = 0;

    // 文件大小
    let size = dv.getUint32(offset, false);
    offset += 4;
    console.log("file size => ", size);

    // 文件类型
    let t = dv.getUint8(offset++);
    let tb = new Uint8Array(b, offset, t);
    offset += t;
    let type = btos(tb);

    // 文件名
    let n = dv.getUint8(offset++);
    let nb = new Uint8Array(b, offset, n);
    offset += n;
    let name = btos(nb);

    // 文件数据
    let fb = new Uint8Array(b, offset);
    offset += size;

    if(offset < b.byteLength) {
        console.error("文件读取发生错误");
        return;
    }

    return new File([fb], name, {type: type});
}

/**
 * 十进制转二进制
 * 依赖于字符编码
 * @param n
 */
function ntob(n) {
    let bytes = new Uint8Array(4);
    for (let i = 0; i < bytes.length; i++) {
        bytes[i] = ((n >> (bytes.length - 1 - i) * 8) & 0xff);
    }
    return bytes;
}

/**
 * 二进制转十进制
 *
 * 二进制字节数要求是4的倍数
 *
 * @param b
 */
function bton(b) {
    return new DataView(b.buffer).getInt32(0);
}

/**
 * 合并二进制
 *
 * @param {Uint8Array} separator
 * @param {Uint8Array} args
 * @returns {Uint8Array}
 */
function concat(separator, ...args) {
    let byteLength = 0, len = args.length;

    for(let i = 0; i < len; ++i) {
        byteLength += args[i].byteLength;
    }

    if(separator) {
        byteLength += separator.byteLength * (len - 1);
    }

    let ui8 = new Uint8Array(byteLength);
    let offset = 0;

    for(let i = 0; i < len - 1; ++i) {
        ui8.set(args[i], offset);
        offset += args[i].byteLength;
        if(separator) {
            ui8.set(separator, offset);
            offset += separator.byteLength;
        }
    }
    ui8.set(args[len - 1], offset);
    offset += args[len - 1].byteLength;

    if(offset !== ui8.byteLength) {
        console.error("合并二进制数组失败！[ byteLength: %d != offset: %d ]", ui8.byteLength, offset);
    }

    return ui8;
}

/**
 * base64转二进制
 * @param url
 */
function dataURL2Uint8Array(url) {
    let arr = url.split(","),
        mime = arr[0].match(/:(.*);/i)[1],
        base64 = arr[1];

    // console.log("mime-type =>", mime);
    // console.log("base64 =>", base64);

    let binstr = window.atob(base64);
    let ui8 = new Uint8Array(binstr.length);
    Array.prototype.forEach.call(binstr, (c, i) => ui8[i] = c.charCodeAt(0));

    return ui8;
}


